package com.google.code.expr;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.Properties;

/**
 * <p>Reader implementation of EXPR.  Accepts an InputStream or Reader,
 * and an optional Properties list of variables.  It applies the EXPR
 * command rules as it reads in the stream, outputting the parsed
 * result as it goes.</p>
 * 
 * @see com.google.code.expr.EXPRInputStream for more details of EXPR.
 * 
 * @author critchie
 *
 */
public class EXPRReader extends Reader {

	private EXPRInputStream mInputStream;
	
	/**
	 * <p>Constructs the EXPR reader using the Reader
	 * parameter as the source.  It will pipe the Reader
	 * through using EXPR parsing.</p>
	 * 
	 * @param pParent the source stream to parse
	 * @throws IOException Either a Reading or EXPR parsing error.
	 */
	public EXPRReader(Reader pParent) throws IOException {
		this(pParent,null);
	}
	
	/**
	 * <p>Constructs the EXPR reader using the Reader
	 * parameter as the source, and the variables from
	 * the Properties parameter.  It will pipe the Reader
	 * through using EXPR parsing.</p>
	 * 
	 * @param pParent the source stream to parse
	 * @param pVariables the initial variables to set
	 * @throws IOException Either a Reading or EXPR parsing error.
	 */
	public EXPRReader(Reader pParent, Properties pVariables) throws IOException {
		mInputStream = new EXPRInputStream(pParent, pVariables);
	}
	
	/**
	 * <p>Constructs the EXPR reader using the InputStream
	 * parameter as the source.  It will pipe the InputStream
	 * through using EXPR parsing.</p>
	 * 
	 * @param pParent the source stream to parse
	 * @throws IOException Either a Reading or EXPR parsing error.
	 */
	public EXPRReader(InputStream pParent) throws IOException {
		this(pParent,null);
	}
	
	/**
	 * <p>Constructs the EXPR reader using the InputStream
	 * parameter as the source, and the variables from
	 * the Properties parameter.  It will pipe the InputStream
	 * through using EXPR parsing.</p>
	 * 
	 * @param pParent the source stream to parse
	 * @param pVariables the initial variables to set
	 * @throws IOException Either a Reading or EXPR parsing error.
	 */
	public EXPRReader(InputStream pParent, Properties pVariables) throws IOException {
		mInputStream = new EXPRInputStream(pParent, pVariables);
	}
	
	
	/**
	 * The main Reader method.  Parses the source stream and writes the
	 * output to the given buffer.  This applies the EXPR parsing as
	 * it reads.
	 */
	@Override public int read(char[] cbuf, int off, int len) throws IOException {
		if (cbuf!=null && off>=0 && off<cbuf.length && len>0 && off+len<=cbuf.length) {
			int count=0;
			int nextChar;
			do {
				nextChar = mInputStream.read();
				if (nextChar>=0) {
					cbuf[off+count]=(char)nextChar;
					count++;
				}
			}
			while (count<len && nextChar>=0);
			return count > 0 ? count : -1;
		}
		return -1;
	}

	/**
	 * Reads the next character from the stream, and returns
	 * -1 if the stream is done. This applies the EXPR parsing as
	 * it reads.
	 */
	@Override public int read() throws IOException {
		return mInputStream.read();
	}

	/**
	 * Closes the internal Reader or Inputstream passed
	 * in.
	 */
	@Override public void close() throws IOException {
		mInputStream.close();
	}

	public static void main(String[] pArgs) throws Exception {
		FileInputStream fis = new FileInputStream("test/simple.expr");

		Properties state = new Properties();
		state.load(new FileInputStream("test/simple.properties"));
		EXPRReader streamReader = new EXPRReader(fis,state);
		
		char[] buffer = new char[10];
		int size = streamReader.read(buffer);
		while (size>=0) {
			System.out.print(new String(buffer,0,size));
			size = streamReader.read(buffer);
		}

	}
}
